package com.lineage.echo;

import com.lineage.config.Config;
import com.lineage.echo.encryptions.Encryption;
import com.lineage.echo.encryptions.PacketPrint;
import com.lineage.server.serverpackets.ServerBasePacket;
import com.lineage.server.types.UByte8;
import com.lineage.server.types.UChar8;
import java.io.IOException;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class PacketSc implements Runnable {
  private static final Log _log = LogFactory.getLog(PacketSc.class);
  
  private static boolean _debug = Config.DEBUG;
  
  private final Queue<byte[]> _queue;
  
  private final ClientExecutor _client;
  
  private final EncryptExecutor _executor;
  
  private Encryption _keys;
  
  public PacketSc(ClientExecutor client, EncryptExecutor executor) {
    this._client = client;
    this._keys = client.get_keys();
    this._executor = executor;
    this._queue = (Queue)new ConcurrentLinkedQueue<>();
  }
  
  private void requestWork(byte[] data) {
    this._queue.offer(data);
  }
  
  public void encrypt(ServerBasePacket packet) throws Exception {
    byte[] encrypt = packet.getContent();
    if (encrypt.length > 0 && this._executor.out() != null) {
      if (_debug)
        _log.info("服务端: " + packet.getType() + "\nOP ID: " + (encrypt[0] & 0xFF) + "\nInfo:\n" + 
            PacketPrint.get().printData(encrypt, encrypt.length)); 
      char[] ac = new char[encrypt.length];
      ac = UChar8.fromArray(encrypt);
      ac = this._keys.encrypt(ac);
      if (ac == null)
        return; 
      encrypt = UByte8.fromArray(ac);
      requestWork(encrypt);
    } 
  }
  
  public void run() {
    try {
      while (this._client.get_socket() != null) {
        Iterator<byte[]> iter = (Iterator)this._queue.iterator();
        while (iter.hasNext()) {
          byte[] decrypt = iter.next();
          iter.remove();
          outPacket(decrypt);
          Thread.sleep(1L);
        } 
        Thread.sleep(10L);
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } finally {
      this._queue.clear();
    } 
  }
  
  private void outPacket(byte[] decrypt) {
    try {
      int outLength = decrypt.length + 2;
      this._executor.out().write(outLength & 0xFF);
      this._executor.out().write(outLength >> 8 & 0xFF);
      this._executor.out().write(decrypt);
      this._executor.out().flush();
    } catch (IOException e) {
      this._executor.stop();
    } 
  }
  
  public void stop() {
    this._queue.clear();
  }
}
